#include "interrupt.h"
#include "global.h"
#include "print.h"
#include "io.h"

#define PIC_USE_8259A

#define PIC_M_CTRL          0x20
#define PIC_M_DATA          0x21

#define PIC_S_CTRL          0xa0
#define PIC_S_DATA          0xa1

#define IDT_DESC_CNT        0x30

#ifdef PIC_USE_8259A

#define PIC_8259A_M_ICW1    PIC_M_CTRL
#define PIC_8259A_M_ICW2    PIC_M_DATA
#define PIC_8259A_M_ICW3    PIC_M_DATA
#define PIC_8259A_M_ICW4    PIC_M_DATA
#define PIC_8259A_M_OCW1    PIC_M_DATA
#define PIC_8259A_M_OCW2    PIC_M_CTRL
#define PIC_8259A_M_OCW3    PIC_M_CTRL

#define PIC_8259A_S_ICW1    PIC_S_CTRL
#define PIC_8259A_S_ICW2    PIC_S_DATA
#define PIC_8259A_S_ICW3    PIC_S_DATA
#define PIC_8259A_S_ICW4    PIC_S_DATA
#define PIC_8259A_S_OCW1    PIC_S_DATA
#define PIC_8259A_S_OCW2    PIC_S_CTRL
#define PIC_8259A_S_OCW3    PIC_S_CTRL


#endif

#define EFLAGS_IF           0x00000200   

struct intr_gate_desc{
    uint16_t seg_offset_low;
    uint16_t selector;
    uint8_t reserved;
    uint8_t attribute;
    uint16_t seg_offset_high;
};

static struct intr_gate_desc idt[IDT_DESC_CNT]; 
char* intr_name[IDT_DESC_CNT];
intr_handler intr_table[IDT_DESC_CNT];
extern intr_handler intr_entry_table[IDT_DESC_CNT];

static void __general_intr_handler(uint8_t vec_nr){
    /* IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理 */
    if (vec_nr == 0x27 || vec_nr == 0x2f) {	
      return;		
   }

    put_str("----------------Exception begin----------------\n");

    put_str(intr_name[vec_nr]);
    put_char('\n');

    if(vec_nr == 0x0e){
        /* 如果是页错误 */
        uint32_t __error_addr =0;
        asm volatile("mov %%cr2, %0" : "=r"(__error_addr) : :);
        put_int_x(__error_addr);
        put_char('\n');
    }

    put_str("----------------Exception end----------------\n"); 
    while(1);
}

/* 创建中断门描述符 */
static void __make_idt_desc(struct intr_gate_desc* p_gdesc, uint8_t attr, intr_handler function) { 
   p_gdesc->seg_offset_low = (uint32_t)function & 0x0000FFFF;
   p_gdesc->selector = SELECTOR_K_CODE;
   p_gdesc->reserved = 0;
   p_gdesc->attribute = attr;
   p_gdesc->seg_offset_high = ((uint32_t)function & 0xFFFF0000) >> 16;
}

/*初始化中断描述符表*/
static void _idt_desc_init(void) {
   uint16_t i;
   put_str("       idt desc init\n");
   for (i = 0; i < IDT_DESC_CNT; i++) {
      __make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); 
   }
}

static void _pic_init(void){
    put_str("       pic init\n");
    /* modify by jianglin 2023-11-07 14:45
     * 修改主pic起始中断号为0x20
     * 修改次pic起始中断号为0x28
     */
    io_out_byte(PIC_8259A_M_ICW1, 0x11);
    io_out_byte(PIC_8259A_M_ICW2, 0x20);
    io_out_byte(PIC_8259A_M_ICW3, 0x04);
    io_out_byte(PIC_8259A_M_ICW4, 0x01);

    io_out_byte(PIC_8259A_S_ICW1, 0x11);
    io_out_byte(PIC_8259A_S_ICW2, 0x28);
    io_out_byte(PIC_8259A_S_ICW3, 0x02);
    io_out_byte(PIC_8259A_S_ICW4, 0x01);
    /* 开启ir0和ir1中断 */
    io_out_byte(PIC_8259A_M_OCW1, 0xfc);
    io_out_byte(PIC_8259A_S_OCW1, 0xff);
}



static void _exception_init(void){
    uint16_t i;
    for(i = 0; i < IDT_DESC_CNT; i++){
        intr_table[i] = __general_intr_handler;
        intr_name[i] = "unknown";
    }
    intr_name[0] = "#DE Divide Error";
    intr_name[1] = "#DB Debug Exception";
    intr_name[2] = "NMI Interrupt";
    intr_name[3] = "#BP Breakpoint Exception";
    intr_name[4] = "#OF Overflow Exception";
    intr_name[5] = "#BR BOUND Range Exceeded Exception";
    intr_name[6] = "#UD Invalid Opcode Exception";
    intr_name[7] = "#NM Device Not Available Exception";
    intr_name[8] = "#DF Double Fault Exception";
    intr_name[9] = "Coprocessor Segment Overrun";
    intr_name[10] = "#TS Invalid TSS Exception";
    intr_name[11] = "#NP Segment Not Present";
    intr_name[12] = "#SS Stack Fault Exception";
    intr_name[13] = "#GP General Protection Exception";
    intr_name[14] = "#PF Page-Fault Exception";
    intr_name[15] = "Intel reserved";
    intr_name[16] = "#MF x87 FPU Floating-Point Error";
    intr_name[17] = "#AC Alignment Check Exception";
    intr_name[18] = "#MC Machine-Check Exception";
    intr_name[19] = "#XF SIMD Floating-Point Exception";
}

enum intr_status intr_get_status(void){
    uint32_t eflags = 0;
    asm volatile("pushfl; pop %0" : "=g"(eflags));
    return (enum intr_status)((eflags & EFLAGS_IF) ? INTR_ON: INTR_OFF);
}

enum intr_status intr_disable(void){
    enum intr_status _old_intr_status = intr_get_status();
    if(_old_intr_status == INTR_ON){
        /* 清除中断标志位IF */
        // todo-jianglin：为什么这里需要通知内存更新呢？
        asm volatile("cli" : : : "memory");
    }
    return _old_intr_status;
}

enum intr_status intr_enable(void){
    enum intr_status _old_intr_status = intr_get_status();
    if(_old_intr_status == INTR_OFF){
        asm volatile("sti");
    }
    return _old_intr_status;
}

enum intr_status intr_set_status(enum intr_status status){
    return (status == INTR_OFF ? intr_disable() : intr_enable());
}

void intr_register_callback(uint8_t vec_num, intr_handler handler){
    intr_table[vec_num] = handler;
}

void idt_init(void){
    put_str("   idt_init start\n");
    _idt_desc_init();
    _pic_init();
    _exception_init();
    uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16));
    asm volatile("lidt %0" : : "m" (idt_operand));
    

    put_str("   idt_init done\n\n");
}
